<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.4.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"vchao.top","root":"/","scheme":"Muse","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":false,"show_result":false,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}};
  </script>

  <meta name="description" content="什么是Overdraw？Overdraw就是过度绘制，是指在一帧的时间内（16.67ms）像素被绘制了多次，理论上一个像素每次只绘制一次是最优的，但是由于重叠的布局导致一些像素会被多次绘制，而每次绘制都会对应到CPU的一组绘图命令和GPU的一些操作，当这个操作耗时超过16.67ms时，就会出现掉帧现象，也就是我们所说的卡顿，所以对重叠不可见元素的重复绘制会产生额外的开销，需要尽量减少Overdra">
<meta property="og:type" content="article">
<meta property="og:title" content="Android解决卡顿，从避免Overdraw开始">
<meta property="og:url" content="http://vchao.top/2021/08/25/Android%E8%A7%A3%E5%86%B3%E5%8D%A1%E9%A1%BF%EF%BC%8C%E4%BB%8E%E9%81%BF%E5%85%8DOverdraw%E5%BC%80%E5%A7%8B/index.html">
<meta property="og:site_name" content="乘风破浪">
<meta property="og:description" content="什么是Overdraw？Overdraw就是过度绘制，是指在一帧的时间内（16.67ms）像素被绘制了多次，理论上一个像素每次只绘制一次是最优的，但是由于重叠的布局导致一些像素会被多次绘制，而每次绘制都会对应到CPU的一组绘图命令和GPU的一些操作，当这个操作耗时超过16.67ms时，就会出现掉帧现象，也就是我们所说的卡顿，所以对重叠不可见元素的重复绘制会产生额外的开销，需要尽量减少Overdra">
<meta property="og:locale" content="zh_CN">
<meta property="article:published_time" content="2021-08-25T14:42:47.353Z">
<meta property="article:modified_time" content="2021-08-25T14:37:20.794Z">
<meta property="article:author" content="vchao">
<meta property="article:tag" content="Android">
<meta property="article:tag" content="优化">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="http://vchao.top/2021/08/25/Android%E8%A7%A3%E5%86%B3%E5%8D%A1%E9%A1%BF%EF%BC%8C%E4%BB%8E%E9%81%BF%E5%85%8DOverdraw%E5%BC%80%E5%A7%8B/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>Android解决卡顿，从避免Overdraw开始 | 乘风破浪</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">乘风破浪</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>

  </li>
  </ul>
</nav>




</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://vchao.top/2021/08/25/Android%E8%A7%A3%E5%86%B3%E5%8D%A1%E9%A1%BF%EF%BC%8C%E4%BB%8E%E9%81%BF%E5%85%8DOverdraw%E5%BC%80%E5%A7%8B/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="vchao">
      <meta itemprop="description" content="怀揣好奇，梦想改变世界的菜鸟极客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="乘风破浪">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          Android解决卡顿，从避免Overdraw开始
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
              

              <time title="创建时间：2021-08-25 22:42:47 / 修改时间：22:37:20" itemprop="dateCreated datePublished" datetime="2021-08-25T22:42:47+08:00">2021-08-25</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Android/" itemprop="url" rel="index"><span itemprop="name">Android</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <h4 id="什么是Overdraw？"><a href="#什么是Overdraw？" class="headerlink" title="什么是Overdraw？"></a>什么是Overdraw？</h4><p>Overdraw就是过度绘制，是指在一帧的时间内（16.67ms）像素被绘制了多次，理论上一个像素每次只绘制一次是最优的，但是由于重叠的布局导致一些像素会被多次绘制，而每次绘制都会对应到CPU的一组绘图命令和GPU的一些操作，当这个操作耗时超过16.67ms时，就会出现掉帧现象，也就是我们所说的卡顿，所以对重叠不可见元素的重复绘制会产生额外的开销，需要尽量减少Overdraw的发生。<br>Android提供了测量Overdraw的选项，在开发者选项－调试GPU过度绘制（Show GPU Overdraw），打开选项就可以看到当前页面Overdraw的状态，就可以观察屏幕的绘制状态。该工具会使用三种不同的颜色绘制屏幕，来指示overdraw发生在哪里以及程度如何，其中：<br>没有颜色： 意味着没有overdraw。像素只画了一次。<br>蓝色： 意味着overdraw 1倍。像素绘制了两次。大片的蓝色还是可以接受的（若整个窗口是蓝色的，可以摆脱一层）。<br>绿色： 意味着overdraw 2倍。像素绘制了三次。中等大小的绿色区域是可以接受的但你应该尝试优化、减少它们。<br>浅红： 意味着overdraw 3倍。像素绘制了四次，小范围可以接受。<br>暗红： 意味着overdraw 4倍。像素绘制了五次或者更多。这是错误的，要修复它们。</p>
<p>那么我们怎么来消灭overdraw呢？总的原则就是：尽量避免重叠不可见元素的绘制，基于这个原则，我们大概可以想出以下几招：</p>
<h4 id="第一招：合理选择控件容器"><a href="#第一招：合理选择控件容器" class="headerlink" title="第一招：合理选择控件容器"></a>第一招：合理选择控件容器</h4><p>既然overdraw是因为重复绘制了同一片区域的像素点，那我们首先想到的是解决布局问题。Android提供的Layout控件主要包括LinearLayout、TableLayout、FrameLayout、RelativeLayout。俗话说条条大路通罗马，同一个界面我们可以使用不同的容器控件来表达，但是各个容器控件描述界面的复杂度是不一样的。一般来说LinearLayout最易，RelativeLayout较复杂。但是尺有所短，寸有所长，LinearLayout只能用来描述一个方向上连续排列的控件，而RelativeLayout几乎可以用于描述任意复杂度的界面。但是我又要说但是了，表达能力越强的容器控件，性能往往略低一些，因为系统需要将更多的时间花在计算子控件的位置上。综上所述：<strong>LinearLayout易用，效率高，表达能力有限。RelativeLayout复杂，表达能力强，效率稍逊。</strong><br>那么对于同一界面而言，作为开发者考虑是使用尽量少的、表达能力强的RelativeLayout作为容器，还是选择多个、表达能力稍弱的LinearLayout来展示。从减少overdraw的角度来看，LinearLayout会增加控件数的层级，自然是RelativeLayout更优，但是当某一界面在使用LinearLayout并不会比RelativeLayout带来更多的控件数和控件层级时，LinearLayout则是首选。所以在表达界面的时候，作为一个有前瞻性的开发者要根据实际情况来选择合适容器控件，在保证性能的同时，尽量避免overdraw。</p>
<h4 id="第二招：去掉window的默认背景"><a href="#第二招：去掉window的默认背景" class="headerlink" title="第二招：去掉window的默认背景"></a>第二招：去掉window的默认背景</h4><p>当我们使用了Android自带的一些主题时，window会被默认添加一个纯色的背景，这个背景是被DecorView持有的。当我们的自定义布局时又添加了一张背景图或者设置背景色，那么DecorView的background此时对我们来说是无用的，但是它会产生一次Overdraw，带来绘制性能损耗。<br>去掉window的背景可以在onCreate()中setContentView()之后调用</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">getWindow().setBackgroundDrawable(null); </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>或者在theme中添加</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">android:windowbackground=&quot;null&quot;；</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h4 id="第三招：去掉其他不必要的背景"><a href="#第三招：去掉其他不必要的背景" class="headerlink" title="第三招：去掉其他不必要的背景"></a>第三招：去掉其他不必要的背景</h4><p>有时候为了方便会先给Layout设置一个整体的背景，再给子View设置背景，这里也会造成重叠，如果子View宽度mach_parent，可以看到完全覆盖了Layout的一部分，这里就可以通过分别设置背景来减少重绘。再比如如果采用的是selector的背景，将normal状态的color设置为“@android:color/transparent”,也同样可以解决问题。这里只简单举两个例子，我们在开发过程中的一些习惯性思维定式会带来不经意的Overdraw，所以开发过程中我们为某个View或者ViewGroup设置背景的时候，先思考下是否真的有必要，或者思考下这个背景能不能分段设置在子View上，而不是图方便直接设置在根View上。</p>
<h4 id="第四招：ClipRect-amp-QuickReject"><a href="#第四招：ClipRect-amp-QuickReject" class="headerlink" title="第四招：ClipRect &amp; QuickReject"></a>第四招：ClipRect &amp; QuickReject</h4><p>为了解决Overdraw的问题，Android系统会通过避免绘制那些完全不可见的组件来尽量减少消耗。但是不幸的是，对于那些过于复杂的自定义的View(通常重写了onDraw方法)，Android系统无法检测在onDraw里面具体会执行什么操作，系统无法监控并自动优化，也就无法避免Overdraw了。但是我们可以通过canvas.clipRect()来帮助系统识别那些可见的区域。这个方法可以指定一块矩形区域，只有在这个区域内才会被绘制，其他的区域会被忽视。这个API可以很好的帮助那些有多组重叠组件的自定义View来控制显示的区域。同时clipRect方法还可以帮助节约CPU与GPU资源，在clipRect区域之外的绘制指令都不会被执行，那些部分内容在矩形区域内的组件，仍然会得到绘制。除了clipRect方法之外，我们还可以使用canvas.quickreject()来判断是否没和某个矩形相交，从而跳过那些非矩形区域内的绘制操作。</p>
<h4 id="第五招：ViewStub"><a href="#第五招：ViewStub" class="headerlink" title="第五招：ViewStub"></a>第五招：ViewStub</h4><p>ViewStub是个什么东西？一句话总结：<strong>高效占位符。</strong><br>我们经常会遇到这样的情况，运行时动态根据条件来决定显示哪个View或布局。常用的做法是把View都写在上面，先把它们的可见性都设为View.GONE，然后在代码中动态的更改它的可见性。这样的做法的优点是逻辑简单而且控制起来比较灵活。但是它的缺点就是，耗费资源。虽然把View的初始可见View.GONE但是在Inflate布局的时候View仍然会被Inflate，也就是说仍然会创建对象，会被实例化，会被设置属性。也就是说，会耗费内存等资源。<br>推荐的做法是使用android.view.ViewStub，ViewStub是一个轻量级的View，它一个看不见的，不占布局位置，占用资源非常小的控件。可以为ViewStub指定一个布局，在Inflate布局的时候，只有ViewStub会被初始化，然后当ViewStub被设置为可见的时候，或是调用了ViewStub.inflate()的时候，ViewStub所向的布局就会被Inflate和实例化，然后ViewStub的布局属性都会传给它所指向的布局。这样，就可以使用ViewStub来方便的在运行时，要还是不要显示某个布局。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&lt;ViewStub</span><br><span class="line">    android:id=&quot;@+id/stub_view&quot;</span><br><span class="line">    android:inflatedId=&quot;@+id/panel_stub&quot;</span><br><span class="line">    android:layout=&quot;@layout/progress_overlay&quot;</span><br><span class="line">    android:layout_width=&quot;fill_parent&quot;</span><br><span class="line">    android:layout_height=&quot;wrap_content&quot;</span><br><span class="line">    android:layout_gravity=&quot;bottom&quot; /&gt;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>当你想加载布局时，可以使用下面其中一种方法：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">((ViewStub) findViewById(R.id.stub_view)).setVisibility(View.VISIBLE);</span><br><span class="line">View importPanel = ((ViewStub) findViewById(R.id.stub_view)).inflate();</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h4 id="第六招：Merge"><a href="#第六招：Merge" class="headerlink" title="第六招：Merge"></a>第六招：Merge</h4><p>Merge标签有什么用呢？简单粗暴点回答：<strong>干掉一个view层级。</strong><br>Merge的作用很明显，但是也有一些使用条件的限制。有两种情况下我们可以使用Merge标签来做容器控件。第一种子视图不需要指定任何针对父视图的布局属性，就是说父容器仅仅是个容器，子视图只需要直接添加到父视图上用于显示就行。另外一种是假如需要在LinearLayout里面嵌入一个布局（或者视图），而恰恰这个布局（或者视图）的根节点也是LinearLayout，这样就多了一层没有用的嵌套，无疑这样只会拖慢程序速度。而这个时候如果我们使用merge根标签就可以避免那样的问题。另外Merge只能作为XML布局的根标签使用，当Inflate以<merge />开头的布局文件时，必须指定一个父ViewGroup，并且必须设定attachToRoot为true。<br>举个简单的例子吧：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">&lt;RelativeLayout </span><br><span class="line">xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot; </span><br><span class="line">xmlns:tools=&quot;http://schemas.android.com/tools&quot; </span><br><span class="line">android:layout_width=&quot;match_parent&quot; </span><br><span class="line">android:layout_height=&quot;match_parent&quot; &gt; </span><br><span class="line">&lt;TextView </span><br><span class="line">android:layout_width=&quot;wrap_content&quot;  </span><br><span class="line">android:layout_height=&quot;wrap_content&quot; </span><br><span class="line">android:text=&quot;merge标签使用&quot; /&gt;</span><br><span class="line">&lt;/RelativeLayout&gt;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>把上面这个XML加载到页面中，布局层级是RelativeLayout-TextView。但是采用下面的方式，把RelativeLayout提换成merge，RelativeLayout这一层级就被干掉了。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">&lt;merge</span><br><span class="line">xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot; </span><br><span class="line">xmlns:tools=&quot;http://schemas.android.com/tools&quot; </span><br><span class="line">android:layout_width=&quot;match_parent&quot; </span><br><span class="line">android:layout_height=&quot;match_parent&quot; &gt;</span><br><span class="line">&lt;TextView  </span><br><span class="line">android:layout_width=&quot;wrap_content&quot; </span><br><span class="line">android:layout_height=&quot;wrap_content&quot; </span><br><span class="line"> android:text=&quot;merge标签使用&quot; /&gt;</span><br><span class="line">&lt;/merge&gt;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h4 id="第七招：善用draw9patch"><a href="#第七招：善用draw9patch" class="headerlink" title="第七招：善用draw9patch"></a>第七招：善用draw9patch</h4><p>给ImageView加一个边框，你肯定遇到过这种需求，通常在ImageView后面设置一张背景图，露出边框便完美解决问题，此时这个ImageView，设置了两层drawable，底下一层仅仅是为了作为图片的边框而已。但是两层drawable的重叠区域去绘制了两次，导致overdraw。<br>优化方案： 将背景drawable制作成draw9patch，并且将和前景重叠的部分设置为透明。由于Android的2D渲染器会优化draw9patch中的透明区域，从而优化了这次overdraw。 但是背景图片必须制作成draw9patch才行，因为Android 2D渲染器只对draw9patch有这个优化，否则，一张普通的Png，就算你把中间的部分设置成透明，也不会减少这次overdraw。</p>
<h4 id="第八招：慎用Alpha"><a href="#第八招：慎用Alpha" class="headerlink" title="第八招：慎用Alpha"></a>第八招：慎用Alpha</h4><p>假如对一个View做Alpha转化，需要先将View绘制出来，然后做Alpha转化，最后将转换后的效果绘制在界面上。通俗点说，做Alpha转化就需要对当前View绘制两遍，可想而知，绘制效率会大打折扣，耗时会翻倍，所以Alpha还是慎用。<br>如果一定做Alpha转化的话，可以采用缓存的方式。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">view.setLayerType(LAYER_TYPE_HARDWARE);</span><br><span class="line">doSmoeThing();</span><br><span class="line">view.setLayerType(LAYER_TYPE_NONE);</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>通过setLayerType方式可以将当前界面缓存在GPU中，这样不需要每次绘制原始界面，但是GPU内存是相当宝贵的，所以用完要马上释放掉。</p>
<h4 id="第九招：避免“OverDesign”"><a href="#第九招：避免“OverDesign”" class="headerlink" title="第九招：避免“OverDesign”"></a>第九招：避免“OverDesign”</h4><p>overdraw会给APP带来不好的体验，overdraw产生的原因无外乎：复杂的Layout层级，重叠的View，重叠的背景这几种。开发人员无节制的View堆砌，究其根本无非是产品无节制的需求设计。有道是“由俭入奢易，由奢入俭难”，很多APP披着过度设计的华丽外衣，却忘了简单易用才是王道的本质，纷繁复杂的设计并不会给用户带来好的体验，反而会让用户有压迫感，产品本身也有可能因此变得卡顿。当然，一切抛开业务谈优化都是空中楼阁，这就需要产品设计也要有一个权衡，在复杂的业务逻辑与简单易用的界面展现中做一个平衡，而不是一味的OverDesign。</p>
<p>本文转载，原文信息如下：<br>作者：尹star<br>链接：<a target="_blank" rel="noopener" href="https://www.jianshu.com/p/145fc61011cd">https://www.jianshu.com/p/145fc61011cd</a><br>來源：简书<br>著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。</p>

    </div>

    
    
    

      <footer class="post-footer">
          <div class="post-tags">
              <a href="/tags/Android/" rel="tag"># Android</a>
              <a href="/tags/%E4%BC%98%E5%8C%96/" rel="tag"># 优化</a>
          </div>

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/2021/08/25/Android%E8%87%AA%E5%AE%9A%E4%B9%89View-%E5%80%92%E8%AE%A1%E6%97%B6%E6%8E%A7%E4%BB%B6%EF%BC%88%E5%8F%AF%E8%87%AA%E5%8A%A8%E7%BB%A7%E7%BB%AD%EF%BC%8C%E7%9F%AB%E6%AD%A3%E6%97%B6%E9%97%B4%EF%BC%89/" rel="prev" title="Android自定义View-倒计时控件（可自动继续，矫正时间）">
      <i class="fa fa-chevron-left"></i> Android自定义View-倒计时控件（可自动继续，矫正时间）
    </a></div>
      <div class="post-nav-item">
    <a href="/2021/08/25/APP%E5%86%85%E5%AD%98%E4%BC%98%E5%8C%96%EF%BC%9A%E4%BD%BF%E7%94%A8LeakCanary%E5%87%8F%E5%B0%91%E5%86%85%E5%AD%98%E6%B3%84%E9%9C%B2/" rel="next" title="APP内存优化：使用LeakCanary减少内存泄露">
      APP内存优化：使用LeakCanary减少内存泄露 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E4%BB%80%E4%B9%88%E6%98%AFOverdraw%EF%BC%9F"><span class="nav-number">1.</span> <span class="nav-text">什么是Overdraw？</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%AC%AC%E4%B8%80%E6%8B%9B%EF%BC%9A%E5%90%88%E7%90%86%E9%80%89%E6%8B%A9%E6%8E%A7%E4%BB%B6%E5%AE%B9%E5%99%A8"><span class="nav-number">2.</span> <span class="nav-text">第一招：合理选择控件容器</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%AC%AC%E4%BA%8C%E6%8B%9B%EF%BC%9A%E5%8E%BB%E6%8E%89window%E7%9A%84%E9%BB%98%E8%AE%A4%E8%83%8C%E6%99%AF"><span class="nav-number">3.</span> <span class="nav-text">第二招：去掉window的默认背景</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%AC%AC%E4%B8%89%E6%8B%9B%EF%BC%9A%E5%8E%BB%E6%8E%89%E5%85%B6%E4%BB%96%E4%B8%8D%E5%BF%85%E8%A6%81%E7%9A%84%E8%83%8C%E6%99%AF"><span class="nav-number">4.</span> <span class="nav-text">第三招：去掉其他不必要的背景</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%AC%AC%E5%9B%9B%E6%8B%9B%EF%BC%9AClipRect-amp-QuickReject"><span class="nav-number">5.</span> <span class="nav-text">第四招：ClipRect &amp; QuickReject</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%AC%AC%E4%BA%94%E6%8B%9B%EF%BC%9AViewStub"><span class="nav-number">6.</span> <span class="nav-text">第五招：ViewStub</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%AC%AC%E5%85%AD%E6%8B%9B%EF%BC%9AMerge"><span class="nav-number">7.</span> <span class="nav-text">第六招：Merge</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%AC%AC%E4%B8%83%E6%8B%9B%EF%BC%9A%E5%96%84%E7%94%A8draw9patch"><span class="nav-number">8.</span> <span class="nav-text">第七招：善用draw9patch</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%AC%AC%E5%85%AB%E6%8B%9B%EF%BC%9A%E6%85%8E%E7%94%A8Alpha"><span class="nav-number">9.</span> <span class="nav-text">第八招：慎用Alpha</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%AC%AC%E4%B9%9D%E6%8B%9B%EF%BC%9A%E9%81%BF%E5%85%8D%E2%80%9COverDesign%E2%80%9D"><span class="nav-number">10.</span> <span class="nav-text">第九招：避免“OverDesign”</span></a></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">vchao</p>
  <div class="site-description" itemprop="description">怀揣好奇，梦想改变世界的菜鸟极客</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">46</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
        <span class="site-state-item-count">4</span>
        <span class="site-state-item-name">分类</span>
      </div>
      <div class="site-state-item site-state-tags">
        <span class="site-state-item-count">27</span>
        <span class="site-state-item-name">标签</span>
      </div>
  </nav>
</div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        
  <div class="beian"><a href="https://beian.miit.gov.cn/" rel="noopener" target="_blank">冀ICP备 17022751号-1 </a>
      <img src="https://beian.miit.gov.cn" style="display: inline-block;">
  </div>

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2021</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">vchao</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://muse.theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/muse.js"></script>


<script src="/js/next-boot.js"></script>




  















  

  

</body>
</html>
